/*
 Copyright (C) 2010-2011. 
 Ezequiel Palumbo, Calipsoide FOSS Foundation.
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU Lesser General Public License for more details.

 You should have received a copy of the GNU Lesser General Public License
 along with this program.  If not, see http://www.gnu.org/licenses/. 
 
 Email: ehpalumbo@gmail.com 
*/ 

package org.calipsoide.gwtspring.support;

import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException;
import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.server.rpc.RPC;
import com.google.gwt.user.server.rpc.RPCRequest;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Servlet that works like a request dispatcher for incoming GWT RPCs. 
 * This is abstract because we want subclasses to be able to provide the 
 * target object to execute RPC over. Subclasses have to implement
 * {@link #findRemoteServiceForCurrentRequest()} in order to provide the
 * service implementation required by each request.
 * 
 * @author Ezequiel Palumbo (ehpalumbo)
 * @since 1.0
 * 
 */
public abstract class GWTDispatcherServlet extends RemoteServiceServlet {

	
	private static final long serialVersionUID = 1L;
	
	protected static final Logger LOGGER = LoggerFactory.getLogger(GWTDispatcherServlet.class); 
	
	
	public GWTDispatcherServlet() {
		super();
	}

	
	protected void service(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		doPost(request, response);
	}

	
	public String processCall(String payload) throws SerializationException {
		String generatedResponse = null;
		LOGGER.debug("Starting incoming RPC process.");
		final Object theRemoteServiceBean = findRemoteServiceForCurrentRequest();
		LOGGER.debug("Found service bean: " + theRemoteServiceBean);
		checkPermutationStrongName();
		try {
			LOGGER.debug("Decoding request payload...");
			RPCRequest rpcRequest = RPC.decodeRequest(payload, null, this);
			LOGGER.debug("Request payload decoded.");
			onAfterRequestDeserialized(rpcRequest);
			LOGGER.debug("Invoking service method and encoding response for client.");
			generatedResponse = RPC.invokeAndEncodeResponse(
					theRemoteServiceBean, 
					rpcRequest.getMethod(), 
					rpcRequest.getParameters(),  
					rpcRequest.getSerializationPolicy(),
			        rpcRequest.getFlags());
			LOGGER.debug("Response generated from service method invocation.");
		} catch (IncompatibleRemoteServiceException irse) {
			LOGGER.error("An IncompatibleRemoteServiceException was thrown "
					+ "while processing this call.", irse);
			LOGGER.debug("Encoding failure response to send back to client.");
			generatedResponse = RPC.encodeResponseForFailure(null, irse);
		} catch (Exception e) {
			LOGGER.error("Could not reply to RPC request; exception was thrown!", e);
			LOGGER.debug("Encoding failure response to send back to client.");
			generatedResponse = RPC.encodeResponseForFailure(null, e);
		}
		LOGGER.debug("Exiting RPC process.");
		return generatedResponse;
	}

	
	/**
	 * Subclasses have to implement this method to provide the remote service 
	 * implementation which will be used to invoke RPC over for the request 
	 * associated with the current thread. You can acquire a reference to it via 
	 * {@link com.google.gwt.user.server.rpc.AbstractRemoteServiceServlet#getThreadLocalRequest()}. 
	 * 
	 * @return The target object to invoke RPC over.
	 */
	protected abstract Object findRemoteServiceForCurrentRequest();

	
}
